/*
 * Copyright 2022 The Open Islands Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openislands.oi.config.stateengine;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.openislands.oi.constant.MessageCodeEnum;
import org.openislands.oi.constant.StateRuleEnum;
import org.openislands.oi.error.LogicException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class StateTransitionAspect {
    private static final Logger log = LoggerFactory.getLogger(StateTransitionAspect.class);

    @Autowired
    private StateEngineContext stateEngineContext;

    /***
     * Intercept all methods at the name state* of all classes under the org.openislands.oi.manager.local package.
     * public modifier. * all return parameters. (..) all parameter lists.
     */
    @Pointcut("execution(public * org.openislands.oi.manager.local.*.state*(..))")
    public void transition() {
    }

    @Before("transition()")
    public void transitionBefore(JoinPoint joinPoint) {
        Object[] pointArgs = joinPoint.getArgs();
        for (Object pointArg : pointArgs) {
            // params contain StateTransition
            if (pointArg instanceof StateTransition) {
                StateTransition st = (StateTransition) pointArg;
                StateRuleEnum stateRuleEnum = st.getStateRuleEnum();
                String currentState = st.getCurrentState();
                String targetState = st.getTargetState();
                if (stateEngineContext.transition(stateRuleEnum, currentState, targetState)) {
                    log.info("state transition pass. {}: {} -> {}", stateRuleEnum, currentState, targetState);
                    break;
                } else {
                    log.warn("state can not transition. {}: {} -> {}", stateRuleEnum, currentState, targetState);
                    throw new LogicException(MessageCodeEnum.PARAMETER_ERROR);
                }
            }
        }
    }

    @After("transition()")
    public void transitionAfter(JoinPoint joinPoint) {
        log.info("state transition done. {}", joinPoint);
    }
}
